#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#############################################################################
# Copyright (c) 2020 Huawei Technologies Co.,Ltd.
#
# openGauss is licensed under Mulan PSL v2.
# You can use this software according to the terms
# and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#
#          http://license.coscl.org.cn/MulanPSL2
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# ----------------------------------------------------------------------------
# Description  : gs_postuninstall is a utility to clean up the environment
# after uninstalling a Gauss200 server.
#############################################################################

import os, sys
import subprocess
import grp
import pwd

package_path = os.path.dirname(os.path.realpath(__file__))
ld_path = package_path + "/gspylib/clib"
if 'LD_LIBRARY_PATH' not in os.environ:
    os.environ['LD_LIBRARY_PATH'] = ld_path
    os.execve(os.path.realpath(__file__), sys.argv, os.environ)
if not os.environ.get('LD_LIBRARY_PATH').startswith(ld_path):
    os.environ['LD_LIBRARY_PATH'] = \
        ld_path + ":" + os.environ['LD_LIBRARY_PATH']
    os.execve(os.path.realpath(__file__), sys.argv, os.environ)


from gspylib.common.GaussLog import GaussLog
from gspylib.common.Common import DefaultValue
from gspylib.common.ErrorCode import ErrorCode
from gspylib.common.ParallelBaseOM import ParallelBaseOM
from gspylib.common.ParameterParsecheck import Parameter
from impl.postuninstall.OLAP.PostUninstallImplOLAP import \
    PostUninstallImplOLAP
from domain_utils.cluster_file.cluster_config_file import ClusterConfigFile
from domain_utils.cluster_file.cluster_dir import ClusterDir
from domain_utils.cluster_file.cluster_log import ClusterLog
from base_utils.os.env_util import EnvUtil
from base_utils.os.net_util import NetUtil
from domain_utils.domain_common.cluster_constants import ClusterConstants
from os_platform.linux_distro import LinuxDistro
from domain_utils.cluster_os.cluster_user import ClusterUser
from domain_utils.cluster_file.profile_file import ProfileFile

class Postuninstall(ParallelBaseOM):
    """
    init the command options
    input : NA
    output: NA
    """

    def __init__(self):
        """
        function: init parameters
        input : NA
        output: NA
        """
        ParallelBaseOM.__init__(self)
        self.deleteUser = False
        self.deleteGroup = False
        self.clean_gphome = False
        self.clean_host = []
        self.sshpwd = ""
        self.nodeList = []
        self.clusterToolPath = ""
        self.userHome = ""
        self.rootPasswd = ""
        self.ips = []
        self.root_ssh_agent_flag = False
        self.gauss_om_path = ""

    def usage(self):
        """
gs_postuninstall is a utility to clean up the environment
after uninstalling the cluster.

Usage:
    gs_postuninstall -? |--help
    gs_postuninstall -V |--version
    gs_postuninstall -U USER -X XMLFILE [-L] [--delete-user] [--delete-group]
                                        [-l LOGFILE]

General options:
    -U                              Cluster user.
    -X                              Path of the XML configuration file.
    -L                              Only clean up local nodes.
        --delete-user               Delete the OS user.
        --delete-group              Delete the group of the OS user.
    -l                              Path of log file.  
    -?, --help                      Show help information for this utility,
                                    and exit the command line mode.
    -V, --version                   Show version information.
        """
        print(self.usage.__doc__)

    def parseCommandLine(self):
        """
        function: Parse command line and save to global variable
        input : NA
        output: NA
        """
        ParaObj = Parameter()
        ParaDict = ParaObj.ParameterCommandLine("postuninstall")
        # check if has '--help' parameter
        if (ParaDict.__contains__("helpFlag")):
            self.usage()
            sys.exit(0)

        # check the parameters of postuninstall command
        if (ParaDict.__contains__("user")):
            self.user = ParaDict.get("user")
        if (ParaDict.__contains__("confFile")):
            self.xmlFile = ParaDict.get("confFile")
        if (ParaDict.__contains__("logFile")):
            self.logFile = ParaDict.get("logFile")

        if (ParaDict.__contains__("delete-user")):
            self.deleteUser = ParaDict.get("delete-user")
        if (ParaDict.__contains__("delete-group")):
            self.deleteGroup = ParaDict.get("delete-group")
        if (ParaDict.__contains__("clean-gphome")):
            self.clean_gphome = ParaDict.get("clean-gphome")
        if (ParaDict.__contains__("nodename")):
            if not "HOST_IP" in os.environ.keys():
                GaussLog.exitWithError(
                    ErrorCode.GAUSS_518["GAUSS_51801"] % "HOST_IP doesn't" +
                    " so -h parameter is not needed.")
            self.clean_host = ParaDict.get("nodename")
            if len(self.clean_host) == 0:
                raise Exception(ErrorCode.GAUSS_500["GAUSS_50004"] % 'h')

        if (ParaDict.__contains__("localMode")):
            self.localMode = ParaDict.get("localMode")

        if "HOST_IP" in os.environ.keys():
            if not ParaDict.__contains__("localMode"):
                if not (ParaDict.__contains__("clean-gphome")
                        and ParaDict.__contains__("nodename")):
                    GaussLog.exitWithError(
                        ErrorCode.GAUSS_518["GAUSS_51801"] % "HOST_IP" +
                        " so you must specify the -L parameter or (-h and "
                        "--clean-gphome) parameters.")
        if ParaDict.__contains__("clean-gphome"):
            if ParaDict.__contains__("localMode") and\
                    ParaDict.__contains__("nodename"):
                GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50005"]
                                       % ("-L", "-h"))

        if (self.deleteGroup == True and self.deleteUser != True):
            GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50001"]
                                   % "-delete-user" + ".")

    def checkParameter(self):
        """
        function: check parameter
        input : NA
        output: NA
        """
        # check user
        self.checkUser()
        # check config file
        ClusterConfigFile.checkConfigFile(self.xmlFile)
        # check log file
        self.logFile = ClusterLog.checkLogFile(self.logFile, self.user, self.xmlFile,
                                               ClusterConstants.UNPREINSTALL_LOG_FILE)
        # check mpprc file if needed, should be done
        # before check preinstall step
        self.checkMpprcFile()
        # check preInstall
        self.checkPreInstall()
        # check group for redhat
        self.checkGroup()

    def checkUser(self):
        """
        function: check the user
        input : NA
        output: NA
        """
        # check if no user
        if (self.user == ""):
            GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50001"] % "U"
                                   + ".")
        # check if is root user
        if (self.user == "root"):
            GaussLog.exitWithError(ErrorCode.GAUSS_503["GAUSS_50301"])

        try:
            ClusterUser.checkUser(self.user, False)
        except Exception as e:
            GaussLog.exitWithError(str(e))

        if (pwd.getpwnam(self.user).pw_uid == 0):
            GaussLog.exitWithError(ErrorCode.GAUSS_503["GAUSS_50302"])


    def checkMpprcFile(self):
        """
        function: check MpprcFile
        input : NA
        output: NA
        """
        # get path of MpprcFile
        self.mpprcFile = EnvUtil.getEnv(DefaultValue.MPPRC_FILE_ENV)

        try:
            # get tool path
            self.clusterToolPath = ClusterDir.getPreClusterToolPath(self.xmlFile)
        except Exception as e:
            GaussLog.exitWithError(str(e))
        # if MpprcFile is null
        if (self.mpprcFile == None):
            self.mpprcFile = ""
        # if MpprcFile is not null
        if (self.mpprcFile != ""):
            # if no MpprcFile, It has no environment separation, the environment variables are in the.bashrc.
            if (not os.path.exists(self.mpprcFile)):
                GaussLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50201"]
                                       % "MPPRC file" + " %s."
                                       % self.mpprcFile)
            # if is not absolute path
            if (not os.path.isabs(self.mpprcFile)):
                GaussLog.exitWithError(ErrorCode.GAUSS_512["GAUSS_51206"]
                                       % self.mpprcFile)

    def checkPreInstall(self):
        """
        function: check preInstall
        input : NA
        output: NA
        """
        # check if agent-mode
        if "HOST_IP" in os.environ.keys():
            # get om_agent path
            agent_path_cmd = "ps aux | grep 'om_agent.py' | grep %s | grep " \
                             "-v grep | head -n 1 | awk '{print $NF}'" % \
                             self.user
            (status, output) = subprocess.getstatusoutput(agent_path_cmd)
            if (status != 0):
                raise Exception(ErrorCode.GAUSS_535["GAUSS_53507"]
                                % agent_path_cmd)
            agent_path = os.path.dirname(output.strip())
            agent_conf_file = os.path.join(agent_path, 'om_agent.conf')
            if not os.path.exists(agent_conf_file):
                raise Exception(ErrorCode.GAUSS_502["GAUSS_50201"]
                                % agent_conf_file)

            # get agent sep_env_file
            with open(agent_conf_file) as fp:
                recordLines = fp.readlines()
            sep_env_file = ""
            for tmp in recordLines:
                if 'sep_env_file' in tmp:
                    sep_env_file = tmp.split("=")[-1].strip()
            if not os.path.exists(sep_env_file):
                raise Exception(
                    ErrorCode.GAUSS_502["GAUSS_50201"] % sep_env_file)

            cmd = "su - %s -c 'source %s && echo $GAUSS_ENV' 2>/dev/null"\
                  % (self.user, sep_env_file)
            (status, output) = subprocess.getstatusoutput(cmd)
            if (status != 0):
                GaussLog.exitWithError(ErrorCode.GAUSS_518["GAUSS_51802"]
                                       % "$GAUSS_ENV" + "Error: \n%s." % output
                                       + "The cmd is %s" % cmd)
            gaussEnv = output.strip()
        else:
            # check if has mpprcFile
            if (self.mpprcFile != "" and os.path.exists(self.mpprcFile)):
                userprofile = self.mpprcFile
            else:
                userprofile = ProfileFile.get_user_bashrc(self.user)
            cmd = "su - %s -c 'source %s && echo $GAUSS_ENV' 2>/dev/null"\
                  % (self.user, userprofile)
            (status, output) = subprocess.getstatusoutput(cmd)
            if (status != 0):
                GaussLog.exitWithError(ErrorCode.GAUSS_518["GAUSS_51802"]
                                       % "$GAUSS_ENV" + "Error: \n%s." % output
                                       + "The cmd is %s" % cmd)
            gaussEnv = output.strip()

        # if gaussEnv is 2, user do not do uninstall before
        if (str(gaussEnv) == "2"):
            GaussLog.exitWithError(ErrorCode.GAUSS_525["GAUSS_52501"]
                                   % "gs_uninstall")
        # if gaussEnv is not 1, user do not do preinstall before
        elif (str(gaussEnv) != "1" and not self.clean_gphome):
            GaussLog.exitWithError(
                ErrorCode.GAUSS_525["GAUSS_52501"] % "gs_preinstall" +
                "If you do preinstall with seperate file mode, please input "
                "sep-env-file before postuninstall. ")
        elif (str(gaussEnv) == "1" or str(gaussEnv) == "2")\
                and self.clean_gphome:
            GaussLog.exitWithError(ErrorCode.GAUSS_525["GAUSS_52501"]
                                   % "'gs_uninstall' or 'gs_postuninstall"
                                     " no clean gphome'")

    def checkGroup(self):
        """
        function: check user group
        input : NA
        output: NA
        """
        try:
            # get user group
            group = grp.getgrgid(pwd.getpwnam(self.user).pw_gid).gr_name
            distname, version, idnum = LinuxDistro.linux_distribution()
            # check if OS version is redhat or Euler
            if (distname in ("redhat", "euleros", "centos", "openEuler", "FusionOS", "H3Linux", "NingOS")):
                if (self.deleteGroup != True and self.deleteUser == True
                        and self.user == group):
                    GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50001"] %
                                           "-delete-group" + ". You must "
                                                             "delete the "
                                                             "group when you "
                                                             "delete the "
                                                             "user which has "
                                                             "the same name "
                                                             "with the group "
                                                             "in redhat.")
        except Exception as e:
            GaussLog.exitWithError(ErrorCode.GAUSS_503["GAUSS_50308"]
                                   + "Failed to obtain the group for %s" %
                                   self.user + "Error:\n%s" % str(e))

    def initGlobals(self):
        """
        function: init Logg file
        input : NA
        output: NA
        """
        self.initLogger("gs_postuninstall")
        self.logger.ignoreErr = True

        try:
            self.logger.log("Parsing the configuration file.", "addStep")
            # get cluster info from xml file
            # Initialize the self.clusterInfo variable
            self.initClusterInfo()
            os.environ[ClusterConstants.TOOL_PATH_ENV] = self.clusterToolPath
            # Initialize the self.sshTool variable
            self.initSshTool(self.clusterInfo.getClusterNodeNames(),
                             DefaultValue.TIMEOUT_PSSH_POSTPREINSTALL)
            self.logger.debug("The cluster's information:\n%s."
                              % str(self.clusterInfo))
            self.logger.log("Successfully parsed the configuration file.",
                            "constant")
        except Exception as e:
            self.logger.logExit(str(e))

        dirName = os.path.dirname(self.logFile)
        self.localLog = os.path.join(dirName, ClusterConstants.LOCAL_LOG_FILE)
        self.userHome = DefaultValue.getUserHome(self.user)
        self.gauss_om_path = "/home/%s/gauss_om" % self.user

if __name__ == '__main__':
    """
    main function
    input : NA
    output: NA 
    """
    if (os.getuid() != 0):
        GaussLog.exitWithError(ErrorCode.GAUSS_501["GAUSS_50104"])

    try:
        postuninstall = Postuninstall()
        postuninstall.parseCommandLine()
        postuninstall.checkParameter()
        postuninstall.initGlobals()

        if len(postuninstall.clusterInfo.getClusterNodeNames()) == 1 and \
                postuninstall.clusterInfo.getClusterNodeNames()[
                    0] == NetUtil.GetHostIpOrName():
            postuninstall.localMode = True
        impl = PostUninstallImplOLAP(postuninstall)

        # Perform the whole extand process
        impl.run()
    except Exception as e:
        GaussLog.exitWithError(str(e))
